package com.loan.system.utils;

import cn.hutool.json.JSONArray;
import cn.hutool.json.JSONUtil;
import com.itextpdf.text.BaseColor;
import com.itextpdf.text.Font;
import com.itextpdf.text.pdf.BaseFont;
import com.itextpdf.text.pdf.PdfContentByte;
import com.itextpdf.text.pdf.PdfReader;
import com.itextpdf.text.pdf.PdfStamper;
import com.loan.common.utils.StringUtils;
import com.loan.system.domain.AssistType;
import com.loan.system.service.IAssistTypeService;
import com.loan.system.domain.pdf.PdfForm;
import com.loan.system.domain.pdf.PdfFormData;
import com.loan.system.domain.pdf.QueryVo;
import com.loan.system.domain.pdf.ResultDomain;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.cli.CommandLine;
import org.apache.commons.cli.CommandLineParser;
import org.apache.commons.cli.DefaultParser;
import org.apache.pdfbox.pdmodel.PDDocument;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import technology.tabula.CommandLineApp;

import java.io.*;
import java.math.BigDecimal;
import java.util.*;

/**
 * @PACKAGE_NAME: com.buss.work.util
 * @CLASSNAME: ReplacePdf
 * @DEACRIPTION:
 * @AUTHOR: GuoChen
 * @DATE: 2024/1/10
 * @WEEK: 周三
 **/
@Slf4j
@Component
public class ReplacePdf {

    @Autowired
    private IAssistTypeService assistTypeService;

    public void main1(String[] args) throws IOException {

        String filePath = "D:\\QuickStart\\Desktop\\ipa\\detail\\wx-detail.pdf";
        String targetFilePath = "D:\\QuickStart\\Desktop\\ipa\\detail\\test_after1.pdf";
        String fontPath = "D:\\QuickStart\\Desktop\\ipa\\detail\\WeChat-Sans-Std-Regular.ttf";
        log.info("处理PDF文件内表格内容，转换成JSON...");
        Map<Integer, List<PdfForm>> map = tranPdfToDomain(filePath);
        ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
        FileInputStream inputStream = null;
        FileOutputStream fileOutputStream = null;
        try {
            //源文件pdf
            File file = new File(filePath);
            //目标文件
            File destFile = new File(targetFilePath);

            ResultDomain resultDomain = new ResultDomain();
            resultDomain.setDealFilePath(destFile.getAbsolutePath());
            resultDomain.setDealFileUrl(destFile.getAbsolutePath());

            inputStream = new FileInputStream(file);
            fileOutputStream = new FileOutputStream(destFile);
            byte[] bytes = new byte[inputStream.available()];
            // 处理PDF金额数据
            log.info("处理收入数据！");
            Map<Integer, List<PdfFormData>> dealInMap = dealNeedChangeItem(map, true, 1400000, resultDomain);
            log.info("处理支出数据！");
            Map<Integer, List<PdfFormData>> dealOutMap = dealNeedChangeItem(map, false, 1200000, resultDomain);

            inputStream.read(bytes);
            PdfReader reader = new PdfReader(bytes);
            //获取pdf页数
            int pageSize = reader.getNumberOfPages();
            log.info("bytes页数：{}", pageSize);
            dealBytes(bytes, outputStream, dealInMap, dealOutMap, resultDomain);
            log.info("数据替换完成...");
            //得到替换后的文件字节
            byte[] byteArray = outputStream.toByteArray();
            //输出
            fileOutputStream.write(byteArray);
        } catch (Exception e) {
            log.error("处理PDF文件出现异常：{}", e.getMessage());
            log.error(e.getMessage(), e);
        } finally {
            try {
                if (fileOutputStream != null) fileOutputStream.close();
                if (inputStream != null) inputStream.close();
                outputStream.close();
            } catch (IOException e) {
                log.error("关闭输入输出流异常：{}", e.getMessage());
                log.error(e.getMessage(), e);
            }
        }
    }

    public ResultDomain dealPdfFun(String filePath,
                                   String targetFilePath,
                                   QueryVo vo) {

        log.info("处理PDF文件内表格内容，转换成JSON...");
        Map<Integer, List<PdfForm>> map = tranPdfToDomain(filePath);
        ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
        FileInputStream inputStream = null;
        FileOutputStream fileOutputStream = null;
        ResultDomain resultDomain = new ResultDomain();

        try {
            AssistType assistType = assistTypeService.selectAssistTypeById(vo.getAssistTypeId());
            resultDomain.setAssistType(assistType);
            //源文件pdf
            File file = new File(filePath);
            //目标文件
            File destFile = new File(targetFilePath);

            resultDomain.setDealFilePath(destFile.getAbsolutePath());
            resultDomain.setDealFileUrl(destFile.getAbsolutePath());

            inputStream = new FileInputStream(file);
            fileOutputStream = new FileOutputStream(destFile);
            byte[] bytes = new byte[inputStream.available()];
            // 处理PDF金额数据
            Map<Integer, List<PdfFormData>> dealInMap = null;
            Map<Integer, List<PdfFormData>> dealOutMap = null;
            if (vo.getDesireInMoney() != null) {
                log.info("处理收入数据！");
                dealInMap = dealNeedChangeItem(map, true, vo.getDesireInMoney(), resultDomain);
            }
            if (vo.getDesireOutMoney() != null) {
                log.info("处理支出数据！");
                dealOutMap = dealNeedChangeItem(map, false, vo.getDesireOutMoney(), resultDomain);
            }

            inputStream.read(bytes);
            PdfReader reader = new PdfReader(bytes);
            //获取pdf页数
            int pageSize = reader.getNumberOfPages();
            log.info("bytes页数：{}", pageSize);
            dealBytes(bytes, outputStream, dealInMap, dealOutMap, resultDomain);
            log.info("数据替换完成...");
            //得到替换后的文件字节
            byte[] byteArray = outputStream.toByteArray();
            //输出
            fileOutputStream.write(byteArray);
        } catch (Exception e) {
            log.error("处理PDF文件出现异常：{}", e.getMessage());
            log.error(e.getMessage(), e);
        } finally {
            try {
                if (fileOutputStream != null) fileOutputStream.close();
                if (inputStream != null) inputStream.close();
                outputStream.close();
            } catch (IOException e) {
                log.error("关闭输入输出流异常：{}", e.getMessage());
                log.error(e.getMessage(), e);
            }
            return resultDomain;
        }
    }


    /**
     * 获取PDF文件数据并转换成JSON
     *
     * @return Map结构数据
     */
    public Map<Integer, List<PdfForm>> tranPdfToDomain(String filePath) {
        Map<Integer, List<PdfForm>> map = new LinkedHashMap();
        try {
            String exportType = "JSON";
            Integer dealPage;
            File file = new File(filePath);
            PDDocument document = PDDocument.load(file);
            int numberOfPages = document.getNumberOfPages();
            log.info("PDF总页数：{}", numberOfPages);
            for (int i = 0; i < numberOfPages; i++) {
                List<PdfForm> list = new ArrayList<>();
                dealPage = i + 1;
                //-f导出格式,默认CSV  (一定要大写)
                //-p 指导出哪页,all是所有
                //path　D:\\1xx.pdf
                //-l 强制使用点阵模式提取PDF　（关键在于这儿）
                String[] argsa = new String[]{"-f=" + exportType, "-p=" + dealPage, filePath, "-l"};
                CommandLineParser parser = new DefaultParser();
                CommandLine cmd = parser.parse(CommandLineApp.buildOptions(), argsa);
                StringBuilder stringBuilder = new StringBuilder();
                new CommandLineApp(stringBuilder, cmd).extractTables(cmd);
                JSONArray array = JSONUtil.parseArray(stringBuilder);
                list = JSONUtil.toList(array, PdfForm.class);
                map.put(dealPage, list);
            }
        } catch (Exception e) {
            log.error("PDF表格内容转实体异常：{}", e.getMessage());
            log.error(e.getMessage(), e);
        }
        return map;
    }

    /**
     * 处理收入金额数据
     *
     * @param map         表单原始数据
     * @param isInDeal    期望收入总额
     * @param desireMoney 期望得到的总额
     * @param result      数据记录
     * @return
     */
    public Map<Integer, List<PdfFormData>> dealNeedChangeItem(Map<Integer, List<PdfForm>> map,
                                                              Boolean isInDeal,
                                                              Integer desireMoney,
                                                              ResultDomain result) {
        AssistType assistType = result.getAssistType();

        String inOrOut = "收入";
        if (!isInDeal) {
            inOrOut = "支出";
        }
        log.info("处理{}金额数据...", inOrOut);
        BigDecimal excludeMoney = new BigDecimal(0); // 过滤不计算的金额
        Map<Integer, List<PdfFormData>> dealMap = new LinkedHashMap<>();
        Integer totalSize = 0;
        if (!map.isEmpty()) {
            for (Integer pageNum : map.keySet()) {
                List<PdfFormData> dealList = new ArrayList<>();
                List<PdfForm> formList = map.get(pageNum);
                if (formList != null && formList.size() > 0) {
                    PdfForm form = formList.get(0);
                    if (form != null) {
                        List<List<PdfFormData>> dataBox = form.getData();
                        if (dataBox != null && dataBox.size() > 0) {
                            for (List<PdfFormData> box : dataBox) {
                                if (box != null && box.size() > 0) {
                                    for (int i = 0; i < box.size(); i++) {
                                        PdfFormData data = box.get(assistType.getDealFlagColumn());
                                        String text = data.getText();
                                        String filterTextColumn = assistType.getFilterTextColumn();
                                        String excludeStr = assistType.getFilterTextContent();
                                        String filterColumnStrs = "";
                                        if (StringUtils.isNotBlank(filterTextColumn)) {
                                            String[] filterColumns = filterTextColumn.split(",");
                                            for (String filterColumn : filterColumns) {
                                                String str = box.get(Integer.parseInt(filterColumn)).getText().replace("[\n\r\t]", "");
                                                filterColumnStrs = String.join(",", str);
                                            }
                                        }

                                        if (StringUtils.equals(text, inOrOut)) {
                                            if (StringUtils.containsAny(filterColumnStrs, excludeStr.split(","))) {
                                                excludeMoney = excludeMoney.add(new BigDecimal(box.get(Integer.parseInt(assistType.getDealTextColumn())).getText()));
                                                break;
                                            }
                                            PdfFormData moneyData = box.get(Integer.parseInt(assistType.getDealTextColumn()));
                                            // moneyData.setName(tranForm);
                                            // 为收入类型，则进行存储
                                            dealList.add(moneyData);
                                        }
                                        break;
                                    }
                                }
                            }
                        }
                    }
                }
                if (!dealList.isEmpty()) {
                    totalSize += dealList.size();
                    dealMap.put(pageNum, dealList);
                }
            }
        }
        log.info("{}金额数据处理完成...", inOrOut);
        log.info("随机生成金额到指定额度...");
        Integer maxScope = (desireMoney / totalSize) * 2; // 生成金额的最大范围(期待金额平均值的2倍)

        // 随机生成的总额
        List<Integer> desireMoneyList = getRandomMoney(inOrOut,
                excludeMoney.intValue(),
                desireMoney,
                totalSize,
                20,
                maxScope);
        Integer moneyFlag = 0;
        BigDecimal originalMoney = new BigDecimal(0); // 原始金额
        BigDecimal dealMoney = excludeMoney; // 处理后金额
        if (dealMap != null && dealMap.size() > 0) {
            for (Integer pageNum : dealMap.keySet()) {
                List<PdfFormData> dataList = dealMap.get(pageNum);
                if (dataList != null && dataList.size() > 0) {
                    for (PdfFormData data : dataList) {
                        originalMoney = originalMoney.add(new BigDecimal(data.getText()));
                        data.setAfterText(desireMoneyList.get(moneyFlag) + ".00");
                        dealMoney = dealMoney.add(new BigDecimal(data.getAfterText()));
                        moneyFlag++;
                    }
                }
            }
        }
        BigDecimal originalTotalMoney = originalMoney.add(excludeMoney);
        log.info("计算的原始{}总金额为：{}", inOrOut, originalTotalMoney.toString());
        if (isInDeal) {
            // 已过滤的金额
            result.setExcludeInMoney(excludeMoney.floatValue());
            result.setOriginalInTotalMoney(originalTotalMoney.floatValue());
            result.setDealInTotalMoney(dealMoney.floatValue());
        } else {
            result.setExcludeOutMoney(excludeMoney.floatValue());
            result.setOriginalOutTotalMoney(originalTotalMoney.floatValue());
            result.setDealOutTotalMoney(dealMoney.floatValue());
        }
        return dealMap;
    }

    /**
     * @param inOrOut      收入/支出
     * @param total        总金额
     * @param excludeMoney 已过滤的金额
     * @param splitCount   个数
     * @param min          最小金额
     * @param max          最大金额
     */
    public List<Integer> getRandomMoney(String inOrOut, Integer excludeMoney, int total, int splitCount, int min, int max) {
        log.info("需要的{}总金额: {}", inOrOut, total);
        log.info("已过滤{}总金额: {}", inOrOut, excludeMoney);
        log.info("除过滤共有{}个数: {}", inOrOut, splitCount);
        log.info("最小金额: {}", min);
        log.info("最大金额: {}", max);
        Integer tempTotal = total;

        List<Integer> moneyList = new ArrayList<Integer>();
        Random random = new Random();
        for (int i = 0; i < splitCount; i++) {
            int num = 0;
            do {
                num = random.nextInt(max);
            } while (num > max || num < min);
            total -= num;
            moneyList.add(num);
        }

        int check_num = 0;
        int check_num_two = 0;

        log.info(Arrays.toString(moneyList.toArray()));

        for (int x : moneyList) {
            check_num = check_num + x;
        }
        int dealMoneyTotal = check_num + excludeMoney;
        log.info("第一次验证总和: {}", dealMoneyTotal);
        if (dealMoneyTotal < tempTotal) {
            log.info("验证总和: {} 小于预期金额，则进行处理...", dealMoneyTotal);
            int stillNeedMoney = (tempTotal - dealMoneyTotal) + (tempTotal / 10); // 仍需的金额数
            int avgAddMoney = stillNeedMoney / moneyList.size(); // 平均每笔收入要增加的金额数
            for (int i = 0; i < moneyList.size(); i++) {
                Integer orginInt = moneyList.get(i);
                Integer newInt = orginInt + avgAddMoney;
                if (newInt > 100 && newInt < 10000) {
                    Integer dealInt = (newInt / 10) * 10;
                    moneyList.set(i, dealInt);
                }
                if (newInt > 10000) {
                    Integer dealInt = (newInt / 100) * 100;
                    moneyList.set(i, dealInt);
                }
            }
        }
        tempTotal = tempTotal + (tempTotal / 10); // 预期值的10%
        if (dealMoneyTotal > tempTotal) {
            log.info("验证总和: {} 大于预期金额的10%，则进行处理...", dealMoneyTotal);
            int stillNeedMoney = (dealMoneyTotal - tempTotal); // 多余的金额数
            int avgDivMoney = stillNeedMoney / moneyList.size(); // 平均每笔支出要减少的金额数
            for (int i = 0; i < moneyList.size(); i++) {
                Integer orginInt = moneyList.get(i);
                if (orginInt > avgDivMoney) {
                    Integer newInt = orginInt - avgDivMoney;
                    if (newInt > 100 && newInt < 10000) {
                        Integer dealInt = (newInt / 10) * 10;
                        moneyList.set(i, dealInt);
                    }
                    if (newInt > 10000) {
                        Integer dealInt = (newInt / 100) * 100;
                        moneyList.set(i, dealInt);
                    }
                }
            }
        }
        for (int x : moneyList) {
            check_num_two = check_num_two + x;
        }
        log.info("再次验证总和: {}", check_num_two + excludeMoney);
        return moneyList;
    }

    /**
     * 处理PDF文件流字节，替换map数据
     *
     * @param bytes        文件字节
     * @param outputStream 输出流
     * @param inMap        处理的收入数据
     * @param outMap       处理的支出数据
     * @throws Exception
     */
    public void dealBytes(byte[] bytes,
                          OutputStream outputStream,
                          Map<Integer, List<PdfFormData>> inMap,
                          Map<Integer, List<PdfFormData>> outMap,
                          ResultDomain resultDomain) throws Exception {
        PdfReader reader = new PdfReader(bytes);
        PdfStamper stamper = new PdfStamper(reader, outputStream);
        PdfContentByte canvas;
        AssistType assistType = resultDomain.getAssistType();

        if (inMap != null && inMap.size() > 0) {
            Float totalInMoney = 0f;
            log.info("修改PDF文件中收入数据内容");
            for (Integer page : inMap.keySet()) {
                canvas = stamper.getOverContent(page);
                List<PdfFormData> dataList = inMap.get(page);
                if (dataList != null) {
                    totalInMoney += updateMoenyText(assistType, dataList, canvas);
                }
            }
            log.info("处理后总收入金额为：{}", totalInMoney + resultDomain.getExcludeInMoney());

        }

        if (outMap != null && outMap.size() > 0) {
            Float totalOutMoney = 0f;
            log.info("修改PDF文件中支出数据内容");
            for (Integer page : outMap.keySet()) {
                canvas = stamper.getOverContent(page);
                List<PdfFormData> dataList = outMap.get(page);
                if (dataList != null) {
                    totalOutMoney += updateMoenyText(assistType, dataList, canvas);
                }
            }
            log.info("处理后总支出金额为：{}", totalOutMoney + resultDomain.getExcludeOutMoney());

        }
        log.info("PDF文件内容修改完毕！");
        stamper.close();
        reader.close();
    }

    /**
     * 修改byte流中的数据
     *
     * @param assistType 配置类数据
     * @param dataList   要修改的数据
     * @param canvas     画图工具
     * @throws Exception
     */
    private Float updateMoenyText(AssistType assistType,
                                  List<PdfFormData> dataList,
                                  PdfContentByte canvas) throws Exception {
        Integer fontSize = assistType.getFontSize();
        Float totalMoney = 0f;
        for (PdfFormData data : dataList) {
            BigDecimal top = new BigDecimal(data.getTop());
            BigDecimal defualtHeight = new BigDecimal(assistType.getDefualtHeight());
            float x = new BigDecimal(data.getLeft()).floatValue();
            float y = defualtHeight.subtract(top).floatValue();
            float width = new BigDecimal(data.getWidth()).floatValue();
            String afterText = data.getAfterText();
            // 计算处理后的总金额
            totalMoney += Float.parseFloat(afterText);

            canvas.saveState();
            String rectangleBackColor = assistType.getRectangleBackColor();
            String[] rectColors = rectangleBackColor.split(",");
            canvas.setColorFill(new BaseColor(Integer.parseInt(rectColors[0]), Integer.parseInt(rectColors[1]), Integer.parseInt(rectColors[2])));
            float xOffValue = x + assistType.getOffsetX();
            float yOffValue = y + assistType.getOffsetY();
            canvas.rectangle(xOffValue, yOffValue, assistType.getModifyBoxWidth(), assistType.getModifyBoxHeight());
            canvas.fill();
            canvas.restoreState();
            //开始写入文本
            canvas.beginText();
            // BaseFont bf = BaseFont.createFont("STSong-Light", "UniGB-UCS2-H", BaseFont.NOT_EMBEDDED);
            BaseFont bf = BaseFont.createFont(assistType.getFontPath(), BaseFont.CP1252, BaseFont.EMBEDDED);
            Font font = new Font(bf, Font.NORMAL, fontSize);
            float textWidth = font.getBaseFont().getWidthPointKerned(afterText, fontSize);
            float xt = this.getLayoutValueByType(assistType.getLayoutType(), x, width, textWidth);
            String fontColor = assistType.getFontColor();
            String[] fontRgbColors = fontColor.split(",");
            canvas.setRGBColorFill(Integer.parseInt(fontRgbColors[0]), Integer.parseInt(fontRgbColors[1]), Integer.parseInt(fontRgbColors[2]));
            //设置字体和大小
            canvas.setFontAndSize(font.getBaseFont(), fontSize);
            //设置字体的输出位置
            canvas.setTextMatrix(xt, y);
            //要输出的text
            canvas.showText(afterText);
            canvas.endText();
        }
        return totalMoney;
    }

    private float getLayoutValueByType(String type, float x, float width, float textWidth) {
        float value = 0f;
        if (StringUtils.equalsIgnoreCase(type, "left")) {
            // 左对齐
            value = x + 2;
        } else if (StringUtils.equalsIgnoreCase(type, "right")) {
            // 右对齐
            value = (x + width) - textWidth; // 设置文字右对齐X轴标点
        } else {
            // 默认居中
            value = (x + (width / 2)) - (textWidth / 2); // 设置文字居中X轴标点
        }
        return value;
    }

}
